package recovery

import (
	"context"
	"gitee.com/sdynasty/yogurt/errors"
	"gitee.com/sdynasty/yogurt/log"
	"gitee.com/sdynasty/yogurt/middleware"
	"runtime"
)

var ErrUnknownRequest = errors.InternalServer("UNKNOWN", "unknown request error")

type HandlerFunc func(ctx context.Context, req, err any) error

type Option func(*options)
type options struct {
	handler HandlerFunc
}

func WithHandler(h HandlerFunc) Option {
	return func(o *options) {
		o.handler = h
	}
}

func Recovery(opts ...Option) middleware.Middleware {
	op := options{
		handler: func(ctx context.Context, req, err any) error {
			return ErrUnknownRequest
		},
	}
	for _, o := range opts {
		o(&op)
	}
	return func(handler middleware.Handler) middleware.Handler {
		return func(ctx context.Context, req any) (reply any, err error) {
			defer func() {
				if rerr := recover(); rerr != nil {
					buf := make([]byte, 64<<10)
					n := runtime.Stack(buf, false)
					buf = buf[:n]
					log.Context(ctx).Errorf("%v: %+v\n%s\n", rerr, req, buf)

					err = op.handler(ctx, req, rerr)
				}
			}()
			return handler(ctx, req)
		}
	}
}
